﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using WebChatApi.Common;
using WebChatApi.Common.Constants;

namespace WebChatApi
{
    /// <summary>
    /// Extension class for mail.
    /// </summary>
    public static class MailExtensions
    {
        /// <summary>
        /// The separators
        /// </summary>
        private static char[] separators = new char[] { ',', ';' };

        #region Extensions email translation

        /// <summary>
        /// To the mail message.
        /// </summary>
        /// <param name="emailMessageObject">The email message object.</param>
        /// <returns>MailMessage.</returns>
        /// <exception cref="OperationFailureException">ToMailMessage</exception>
        public static MailMessage ToMailMessage(this EmailMessage emailMessageObject)
        {
            MailMessage result = null;

            if (emailMessageObject != null)
            {
                try
                {
                    result = new MailMessage();
                    emailMessageObject.FillMailAddressCollection(result.To, emailMessageObject.ToList);
                    emailMessageObject.FillMailAddressCollection(result.CC, emailMessageObject.CCList);
                    emailMessageObject.FillMailAddressCollection(result.Bcc, emailMessageObject.BCCList);
                    MailAddress address = ValidateMailAddress(emailMessageObject.From);
                    if (address != null)
                    {
                        result.From = address;

                    }
                    result.Subject = emailMessageObject.Subject;
                    result.IsBodyHtml = emailMessageObject.IsBodyHtml;
                    result.FillHeaders(emailMessageObject.Headers);
                    result.BodyEncoding = GetMailEncoding(emailMessageObject.BodyEncoding);
                    result.HeadersEncoding = GetMailEncoding(emailMessageObject.HeadersEncoding);
                    result.Body = emailMessageObject.Body;
                    result.Priority = emailMessageObject.Priority.ToMailPriority();
                    if (!string.IsNullOrWhiteSpace(emailMessageObject.ReplyToList))
                    {
                        foreach (var one in emailMessageObject.ReplyToList.Split(separators, StringSplitOptions.RemoveEmptyEntries))
                        {
                            result.ReplyToList.Add(new MailAddress(one));
                        }
                    }
                    address = ValidateMailAddress(emailMessageObject.SenderAddress);
                    if (address != null)
                    {
                        result.Sender = address;

                    }
                    if (result.Sender == null)
                    {
                        result.Sender = result.From;
                    }

                    if (emailMessageObject.Attachments != null && emailMessageObject.Attachments.Count > 0)
                    {
                        foreach (var one in emailMessageObject.Attachments)
                        {
                            result.Attachments.Add(one.ToAttachment());
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new OperationFailureException("ToMailMessage", ex, emailMessageObject);
                }
            }

            return result;
        }

        /// <summary>
        /// To the email message.
        /// </summary>
        /// <param name="mailMessageObject">The mail message object.</param>
        /// <returns>EmailMessage.</returns>
        public static EmailMessage ToEmailMessage(this MailMessage mailMessageObject)
        {
            EmailMessage result = null;

            if (mailMessageObject != null)
            {
                result = new EmailMessage();
                result.ToList = mailMessageObject.To.ToString();
                result.CCList = mailMessageObject.CC.ToString();
                result.BCCList = mailMessageObject.Bcc.ToString();
                result.Subject = mailMessageObject.Subject;
                result.SenderAddress = mailMessageObject.Sender.ToString();
                result.From = mailMessageObject.From.ToString();
                result.BodyEncoding = mailMessageObject.BodyEncoding.WebName;
                result.IsBodyHtml = mailMessageObject.IsBodyHtml;
                result.Body = mailMessageObject.Body;
                result.HeadersEncoding = mailMessageObject.HeadersEncoding.WebName;
                result.Headers = mailMessageObject.Headers.ToKeyValueString(true);
                result.Attachments = new List<Binary>();
                result.Priority = mailMessageObject.Priority.ToTaskPriority();
                List<string> replyToList = new List<string>();
                foreach (var one in mailMessageObject.ReplyToList)
                {
                    replyToList.Add(one.ToString());
                }
                result.ReplyToList = replyToList.Join(",");

                if (mailMessageObject.Attachments != null && mailMessageObject.Attachments.Count > 0)
                {
                    foreach (var one in mailMessageObject.Attachments)
                    {
                        result.Attachments.Add(one.ToBinary());
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// To the binary.
        /// </summary>
        /// <param name="attachmentObject">The attachment object.</param>
        /// <returns>Binary.</returns>
        public static Binary ToBinary(this Attachment attachmentObject)
        {
            Binary result = null;

            if (attachmentObject != null)
            {
                result = new Binary();
                result.Bytes = attachmentObject.ContentStream.ToBytes();
                result.Extension = Path.GetExtension(attachmentObject.Name);
                result.FileName = Path.GetFileNameWithoutExtension(attachmentObject.Name);
            }

            return result;
        }

        /// <summary>
        /// To the attachment.
        /// </summary>
        /// <param name="binaryObject">The binary object.</param>
        /// <returns>Attachment.</returns>
        public static Attachment ToAttachment(this Binary binaryObject)
        {
            Attachment result = null;

            if (binaryObject != null && binaryObject.Bytes.Length > 0)
            {
                Stream stream = binaryObject.Bytes.ToStream();
                result = new Attachment(stream, binaryObject.CombineExtension(binaryObject.FileName, binaryObject.Extension));
                result.ContentType = ContentTypeCollection.GenerateContentType(MimeTypes.GetMimeType(binaryObject.Extension));
            }

            return result;
        }

        /// <summary>
        /// To the mail addresses.
        /// </summary>
        /// <param name="emailMessageObject">The email message object.</param>
        /// <param name="addressString">The address string.</param>
        /// <returns>MailAddressCollection.</returns>
        /// <exception cref="Gbi.InvalidObjectException">emailAddress;null</exception>
        /// <exception cref="Gbi.OperationFailureException">Convert address string to mail address</exception>
        public static MailAddressCollection ToMailAddresses(this EmailMessage emailMessageObject, string addressString)
        {
            MailAddressCollection result = new MailAddressCollection();
            FillMailAddressCollection(emailMessageObject, result, addressString);
            return result;
        }

        /// <summary>
        /// Fills the mail address collection.
        /// </summary>
        /// <param name="emailMessageObject">The email message object.</param>
        /// <param name="mailAddressCollection">The mail address collection.</param>
        /// <param name="addressString">The address string.</param>
        /// <exception cref="InvalidObjectException">emailAddress;null</exception>
        /// <exception cref="OperationFailureException">Convert address string to mail address</exception>
        public static void FillMailAddressCollection(this EmailMessage emailMessageObject, MailAddressCollection mailAddressCollection, string addressString)
        {
            if (mailAddressCollection != null)
            {
                try
                {
                    if (!string.IsNullOrWhiteSpace(addressString))
                    {
                        string[] emailAddresses = addressString.Split(separators, StringSplitOptions.RemoveEmptyEntries);
                        foreach (var one in emailAddresses)
                        {
                            MailAddress email = ValidateMailAddress(one);
                            if (email == null)
                            {
                                throw new InvalidObjectException("emailAddress", null, one);
                            }
                            mailAddressCollection.Add(new MailAddress(one));
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new OperationFailureException("Convert address string to mail address", ex, addressString);
                }
            }
        }

        /// <summary>
        /// To the mail addresses.
        /// </summary>
        /// <param name="mailMessageObject">The mail message object.</param>
        /// <param name="mailAddressCollection">The mail address collection.</param>
        /// <returns>System.String.</returns>
        public static string ToMailAddresses(this MailMessage mailMessageObject, MailAddressCollection mailAddressCollection)
        {
            return (mailAddressCollection != null) ? mailAddressCollection.ToString() : string.Empty;
        }

        /// <summary>
        /// Fills the headers.
        /// </summary>
        /// <param name="mailMessageObject">The mail message object.</param>
        /// <param name="headerString">The header string.</param>
        public static void FillHeaders(this MailMessage mailMessageObject, string headerString)
        {
            if (mailMessageObject != null)
            {
                mailMessageObject.Headers.FillValuesByKeyValueString(headerString);
            }
        }

        #endregion

        /// <summary>
        /// Validates the mail address.
        /// </summary>
        /// <param name="mailAddress">The mail address.</param>
        /// <returns>MailAddress.</returns>
        public static MailAddress ValidateMailAddress(string mailAddress)
        {
            try
            {
                return new MailAddress(mailAddress);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Gets the encoding.
        /// If failed to get, return UTF-8
        /// </summary>
        /// <param name="webName">Name of the web.</param>
        /// <returns>Encoding.</returns>
        public static Encoding GetMailEncoding(string webName)
        {
            Encoding result = null;

            try
            {
                result = Encoding.GetEncoding(webName);
            }
            catch { }

            if (result == null)
            {
                result = Encoding.UTF8;
            }

            return result;
        }

        /// <summary>
        /// Mails the priority.
        /// </summary>
        /// <param name="priorityObject">The priority object.</param>
        /// <returns>TaskPriority.</returns>
        public static TaskPriority ToTaskPriority(this MailPriority priorityObject)
        {
            TaskPriority result = TaskPriority.Normal;
            Enum.TryParse<TaskPriority>(priorityObject.ToString(), out result);
            return result;
        }

        /// <summary>
        /// To the mail priority.
        /// </summary>
        /// <param name="priorityObject">The priority object.</param>
        /// <returns>MailPriority.</returns>
        public static MailPriority ToMailPriority(this TaskPriority priorityObject)
        {
            MailPriority result = MailPriority.Normal;

            if (priorityObject == TaskPriority.Urgent)
            {
                priorityObject = TaskPriority.High;
            }

            Enum.TryParse<MailPriority>(priorityObject.ToString(), out result);
            return result;
        }
    }
}
